package net.ermannofranco.genericdaojdbc.dao;

import java.util.List;
import java.util.Map;

import net.ermannofranco.genericdaojdbc.clause.Clause;
import net.ermannofranco.genericdaojdbc.clause.ComparationClause;
import net.ermannofranco.genericdaojdbc.clause.HavingClause;
import net.ermannofranco.genericdaojdbc.model.Entity;
import net.ermannofranco.genericdaojdbc.util.Comparation;
import net.ermannofranco.genericdaojdbc.util.Order;
import net.ermannofranco.genericdaojdbc.util.StatFunc;

import org.springframework.context.ApplicationContextAware;

/**
 * Generic Dao Pattern.
 * 
 * @author ermanno (ermanno.franco@gmail.com)
 * 
 * @param <T>
 *            Entity Pojo.
 * @param <K>
 *            Numeric primary key, extends Number.
 * 
 */
public interface GenericDao<T extends Entity<K>, K extends Number> extends
		DataSourceAware, ApplicationContextAware {

	String COUNT_ALL = " COUNT(*) ";
	String SELECT = "SELECT ";
	String FROM = " FROM ";
	String SELECT_ALL_FROM = "SELECT * FROM ";
	String DELETE_FROM = "DELETE FROM ";
	String UPDATE = "UPDATE ";
	String INSERT = "INSERT INTO ";
	String VALUES = " VALUES ";
	String SET = " SET ";
	String WHERE = " WHERE ";
	String AND = " AND ";
	String OR = " OR ";
	String STAR="*";
	String SEMICOLON = ";";
	String COLON = ":";
	String COMMA = ",";
	String SPACE = " ";
	String COMMA_W_SPACE = ", ";
	String EQUALS_COLON = " = :";
	String IN = " IN ";
	String BRACKET_OPEN = "(";
	String BRACKET_CLOSED = ")";
	String ORDER_BY = " ORDER BY ";
	String GROUP_BY = " GROUP BY ";
	String HAVING = " HAVING ";
	String ID = "id";
	String ID_WITH_COLON = ":id";
	String ASC = " ASC ";
	String DESC = " DESC ";

	/**
	 * Oggetto di convenienza per query senza parametri
	 */
	Object EMPTY_ARR = new Object[0];

	/**
	 * Retrieve entity given primary key.
	 * 
	 * @param id
	 * @return entity.
	 */
	T getById(K id);

	/**
	 * Return all the entities.
	 * 
	 * @return all the entities.
	 */
	List<T> listAll();

	/**
	 * return all the entities, ordered by pojo properties
	 * 
	 * @param orderFields
	 *            Order objects, a property and an order
	 * @return
	 */
	List<T> listAllOrdered(Order... orderFields);

	/**
	 * Multiple return, given one or more primary keys.
	 * 
	 * @param ids
	 * @return
	 */
	List<T> listByIds(K... ids);

	/**
	 * 
	 * @param propName
	 * @param value
	 * @return
	 */
	List<T> filter(String propName, Object value);

	/**
	 * 
	 * @param propName
	 * @param value
	 * @param orderFields
	 * @return
	 */
	List<T> filter(String propName, Object value, Order... orderFields);

	/**
	 * All first level clauses are assembled with AND token.
	 * 
	 * @param clauses
	 * @return
	 */
	List<T> filter(Clause... clauses);

	/**
	 * All first level clauses are assembled with AND token.
	 * 
	 * @param clauses
	 * @param orderFields
	 * @return
	 */
	List<T> filter(Clause[] clauses, Order... orderFields);

	/**
	 * @deprecated use filter(...)
	 * @param propName
	 * @param value
	 * @param orderFields
	 * @return
	 */
	 List<T> listByPropertyValue(String propName, Object value,
	 Order... orderFields);
	
	 /**
	  * @deprecated use filter(...)
	  * @param propName
	  * @param c
	  * @param value
	  * @param orderFields
	  * @return
	  */
	 List<T> listByPropertyValue(String propName, Comparation c, Object value,
	 Order... orderFields);
	
	 /**
	  * @deprecated use filter(...)
	  * @param propName
	  * @param value
	  * @return
	  */
	 List<T> listByPropertyValue(String propName, Object value);
	
	 /**
	  * @deprecated use filter(...)
	  * @param propName
	  * @param c
	  * @param value
	  * @return
	  */
	 List<T> listByPropertyValue(String propName, Comparation c, Object
	 value);

	// SISTEMARE
	// List<T> listByIdsOrderBy(String orderField, K... ids);

	/**
	 * Remove given entities, retrieved by ids, from db.
	 */
	boolean remove(K... ids);

	/**
	 * Remove all the entities
	 * 
	 * @return true if at least one is removed.
	 */
	boolean removeAll();

	/**
	 * Insert a new entity in db.
	 * 
	 * @param t
	 * @return Primary key if the id is auto-generated, else 1 or 0 depend on
	 *         the successful insertion.
	 */
	Number insert(T t);

	/**
	 * Batch insert of an entity list.
	 * 
	 * @param list
	 * @return array of 1 or 0, depend on the successful insertion.
	 */
	int[] insertBatch(List<T> list);

	/**
	 * Update an entity with new values.
	 * 
	 * @param t
	 * @return 1 or 0.
	 */
	int update(T t);

	/**
	 * Count all the entities.
	 * 
	 * @return
	 */
	long countAll();

	/**
	 * Statistics function upon a property, filtered by clause on that or
	 * another property. If invoking StatFunc.COUNT, you can alternatively put
	 * "*" instead of a propName, as second parameter.
	 * Example: average of users age whose email starts with e:
	 * result = userDao.calcByProperty(StatFunc.AVG,
				"eta", Clause.like("email", "e%"));
	 * 
	 * @param function Statistic function, as enumerated.
	 * @param propNameToFunction Java numeric property to calculate. 
	 * @param clause, maybe null to avoid filtering.
	 * @return result
	 */
	long calcByProperty(StatFunc function, String propNameToFunction,
			ComparationClause clause);

	/**
	 * Count all the entities, filtered by value of given java property.
	 * 
	 * @param propName
	 * @param value
	 * @return
	 */
	long countByPropertyValue(String propName, Object value);

	/**
	 * Count all the entities, filtered by value of given java property and
	 * comparation (e.g. >= value).
	 * 
	 * @param propName
	 * @param value
	 * @return
	 */
	long countByPropertyValue(String propName, Comparation c, Object value);

	/**
	 * Group query, every entry is a map of columnName->columnValue.
	 * 
	 * @param propName
	 *            Java property to be grouped.
	 * @param countColumnName
	 *            count alias, is the name of result column.
	 * @return list.
	 */
	List<Map<String, Object>> countGroupedByPropertyName(String propName,
			String countColumnName);

	/**
	 * Group query, every entry is a map of columnName->columnValue.
	 * 
	 * @param propName
	 *            Java property to be grouped.
	 * @param countColumnName
	 *            count alias, is the name of result column.
	 * @param where
	 *            clauses to restrict count query.
	 * @return
	 */
	List<Map<String, Object>> countGroupedByPropertyName(String propName,
			String countColumnName, Clause... where);

	/**
	 * 
	 * Group query, every entry is a map of columnName->columnValue.
	 * 
	 * @param propName
	 *            Java property to be grouped.
	 * @param countColumnName
	 *            count alias, is the name of result column.
	 * @param having
	 *            clause to restrict count result.
	 * @param where
	 *            clauses to restrict count query.
	 * @return
	 */
	List<Map<String, Object>> countGroupedByPropertyName(String propName,
			String countColumnName, HavingClause having, Clause... where);

}
